package com.zhuanzhuan.hero.user.interceptor;

import com.zhuanzhuan.hero.user.util.EncryptDecryptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.HashSet;
import java.util.Objects;

/**
 * @ClassName EncryptInterceptor
 * @Description  加密拦截器
 *   所有的insert、update都会将入参字段进行加密；经过测试，单字符串类型是可以的，这里只处理单字符串的;list类型无法生效，使用aop拦截service层方法来补偿
 * @Author hanjunjun
 * @Date 2024/7/22 19:52
 */
@Component
@Slf4j
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)})
public class EncryptInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        //拦截 ParameterHandler 的 setParameters 方法 动态设置参数
        if (target instanceof ParameterHandler) {
            ParameterHandler parameterHandler = (ParameterHandler)target;

            // 反射获取 参数方法
            Class<? extends ParameterHandler> aClass = parameterHandler.getClass();
            Method getParameterObject = aClass.getDeclaredMethod("getParameterObject");
            Object parameterObject = getParameterObject.invoke(parameterHandler);

            if (Objects.nonNull(parameterObject)) {
                //mybatis-plus 框架默认多参数
                if (parameterObject instanceof MapperMethod.ParamMap){
                    MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameterObject;
                    //避免相同的key重复执行，使用set对象记录堆栈地址
                    HashSet<Integer> stackSet = new HashSet<>();
                    for (Object key : paramMap.keySet()) {
                        //需要使用hashcode判断，equals()方法可能被重写。
                        if (stackSet.add(System.identityHashCode(paramMap.get(key)))) {
                            //给对象加密
                            EncryptDecryptUtil.objectEncrypt(paramMap.get(key), true);
                        }
                    }
                } else {
                    EncryptDecryptUtil.objectEncrypt(parameterObject, true);
                }
            }
        }
        return invocation.proceed();
    }
}
